Skip to content

feat: Wire heatmap chart into dashboard editor and tile rendering#2107

Merged
kodiakhq[bot] merged 39 commits intomainfrom
cursor/heatmap-chart-editor-dashboard-5801
Apr 28, 2026
Merged

feat: Wire heatmap chart into dashboard editor and tile rendering#2107
kodiakhq[bot] merged 39 commits intomainfrom
cursor/heatmap-chart-editor-dashboard-5801

Conversation

@alex-fedotyev
Copy link
Copy Markdown
Contributor

@alex-fedotyev alex-fedotyev commented Apr 11, 2026

What

Wires the existing DBHeatmapChart component and DisplayType.Heatmap enum into the chart editor, dashboard tile rendering, and unifies display settings across all heatmap contexts (search Event Deltas, chart editor, dashboards).

Why

Users should be able to create and view heatmap charts from the chart editor and dashboards, alongside existing chart types. Heatmaps visualize distributions over time (e.g., latency distributions) and were previously only available in the search Event Deltas view.

Changes

Shared Display Settings Drawer (HeatmapSettingsDrawer.tsx):

  • Extracted from the inline drawer in DBSearchHeatmapChart.tsx into a reusable component
  • Contains Scale (Log/Linear), Value expression, Count expression
  • Used by search Event Deltas (gear icon), chart editor, and dashboards
  • defaultValues memoized at call sites to prevent unnecessary form resets

Chart Editor (EditTimeChartForm, ChartEditorControls, HeatmapSeriesEditor):

  • Added Heatmap tab with IconGrid3x3 icon
  • Heatmap editor shows Where clause + Display Settings button (opens shared drawer)
  • Single series constraint enforced; validation requires value expression
  • Auto-populates getDurationMsExpression(source) and count() when a Trace source is selected (on tab switch or source change)
  • Sets numberFormat: { output: 'duration', factor: 0.001 } for proper Y-axis labels
  • Generated SQL section hidden for heatmap (queries are built internally by HeatmapContainer)

Dashboard Tiles (DBDashboardPage):

  • HeatmapTile component renders DBHeatmapChart via toHeatmapChartConfig() helper
  • autoRun passed to EditTimeChartForm in dashboard edit modal so saved charts preview immediately
  • numberFormat and scaleType passed through from saved config

Chart Preview (ChartPreviewPanel):

  • HeatmapPreview component renders heatmap in the chart editor preview area

Heatmap UX improvements (DBHeatmapChart.tsx):

  • Dynamic Y-axis sizing via ctx.measureText() — measures actual formatted tick labels
  • Compact tick formatter (formatDurationMsCompact): m instead of min, values under 2min stay as seconds, fewer decimals
  • X-axis: gap: 10 + space: 60 prevents label overlap in narrow containers
  • padding: [8, 8, 0, 4] prevents label clipping at edges
  • Drag-to-select and "Click & Drag" prompt disabled when onFilter is not provided (editor/dashboard); hover tooltip still shows for inspection
  • Full interactivity preserved in search Event Deltas where onFilter is wired

Config helper (toHeatmapChartConfig):

  • Shared function converts builder config to HeatmapChartConfig format
  • Eliminates duplicated config construction and unsafe casts
  • Exported alongside HeatmapChartConfig type from DBHeatmapChart.tsx
Open in Web Open in Cursor 

- Add Heatmap tab to chart editor form with IconGrid3x3 icon
- Create HeatmapSeriesEditor with value expression (Y-axis) and
  count expression (intensity) inputs
- Add heatmap display type to displayTypeToActiveTab mapping
- Add heatmap preview rendering in ChartPreviewPanel
- Add heatmap tile rendering in DBDashboardPage
- Add heatmap validation: single series, required value expression
- Constrain heatmap to single series (no Add Series / ratio)

Co-authored-by: Alex Fedotyev <[email protected]>
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 11, 2026

🦋 Changeset detected

Latest commit: 0ee9fa5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@hyperdx/app Minor
@hyperdx/common-utils Patch
@hyperdx/api Minor
@hyperdx/otel-collector Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview, Comment Apr 28, 2026 8:31pm

Request Review

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 11, 2026

E2E Test Results

All tests passed • 159 passed • 3 skipped • 1192s

Status Count
✅ Passed 159
❌ Failed 0
⚠️ Flaky 4
⏭️ Skipped 3

Tests ran across 4 shards in parallel.

View full report →

…ormat, reduce chart whitespace

- When switching to Heatmap with a trace source, auto-fill value
  expression with getDurationMsExpression and set numberFormat to
  duration with factor 0.001 for proper Y-axis labels (4ms, 100ms, 1.2s)
- Pass numberFormat from config through to DBHeatmapChart in both
  ChartPreviewPanel and DBDashboardPage for correct tick formatting
- Reduce Stack gap in ChartContainer from default to 4px to minimize
  whitespace above the chart area

Co-authored-by: Alex Fedotyev <[email protected]>
…ctivity in editor/dashboard

- Add effect to populate duration defaults when source changes while
  already in heatmap mode (not just when switching to heatmap tab)
- Disable drag-to-select and hide 'Click & Drag' prompt in Heatmap
  when onFilter is not provided (chart editor and dashboard contexts)
- Keep hover tooltip with Y/Count values for read-only inspection

Co-authored-by: Alex Fedotyev <[email protected]>
…ngs button for heatmap

- Fill Value field with getDurationMsExpression and Count field with
  count() when switching to Heatmap with a trace source (visible text,
  not just behind the scenes)
- Add Display Settings button below heatmap editor, same pattern as
  other chart types, opening ChartDisplaySettingsDrawer for number
  format configuration

Co-authored-by: Alex Fedotyev <[email protected]>
…hboards

- Extract HeatmapSettingsDrawer into shared component used by both
  search Event Deltas and chart editor/dashboard contexts
- Remove Value/Count fields from the main heatmap editor form — move
  them into the Heatmap Settings drawer (Scale, Value, Count)
- Replace 'Display Settings' button with 'Heatmap Settings' button
  that opens the same drawer as search Event Deltas gear icon
- Pass scaleType (log/linear) from form state through to DBHeatmapChart
  in preview panel and dashboard tiles
- Pre-populate Value with getDurationMsExpression and Count with count()
  from data source config, editable via Heatmap Settings

Co-authored-by: Alex Fedotyev <[email protected]>
…artContainer gap

- Add padding: [8,0,0,0] to uPlot options to reduce whitespace above
  the topmost Y-axis tick in heatmap charts
- Set lockScroll=false on HeatmapSettingsDrawer to prevent layout shift
  when opening settings inside a dashboard edit modal
- Restore ChartContainer gap to 'xs' (was incorrectly set to 4px
  affecting all chart types)

Co-authored-by: Alex Fedotyev <[email protected]>
…ChartContainer changes

- Wrap HeatmapSettingsDrawer in Portal so it doesn't participate in
  the flex layout of the editor form, preventing content shift when
  opening/closing settings in dashboard edit modal
- Revert ChartContainer gap/margin changes to preserve existing
  spacing for all chart types

Co-authored-by: Alex Fedotyev <[email protected]>
cursoragent and others added 2 commits April 13, 2026 16:46
Pass autoRun to EditTimeChartForm in the dashboard edit modal so
saved charts (including heatmaps) render their preview immediately
when opened for editing, matching the Chart Explorer behavior.

Co-authored-by: Alex Fedotyev <[email protected]>
… layout shift

Move the HeatmapSettingsDrawer out of HeatmapSeriesEditor and render
it at the EditTimeChartForm top level (same pattern as
ChartDisplaySettingsDrawer). This prevents any DOM insertion from
affecting the flex layout between the settings button and run button
when the drawer opens/closes.

Co-authored-by: Alex Fedotyev <[email protected]>
@alex-fedotyev alex-fedotyev marked this pull request as ready for review April 13, 2026 17:30
@github-actions github-actions Bot added the review/tier-3 Standard — full human review required label Apr 13, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 13, 2026

🔴 Tier 4 — Critical

Touches auth, data models, config, tasks, OTel pipeline, ClickHouse, or CI/CD.

Why this tier:

  • Large diff: 1342 production lines changed (threshold: 1000)
  • Cross-layer change: touches frontend (packages/app) + shared utils (packages/common-utils)

Review process: Deep review from a domain expert. Synchronous walkthrough may be required.
SLA: Schedule synchronous review within 2 business days.

Stats
  • Production files changed: 12
  • Production lines changed: 1342 (+ 179 in test files, excluded from tier calculation)
  • Branch: cursor/heatmap-chart-editor-dashboard-5801
  • Author: alex-fedotyev

To override this classification, remove the review/tier-4 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 13, 2026

PR Review

  • ⚠️ HeatmapTile (135 lines) added inline to DBDashboardPage.tsx violates the project's "files under 300 lines" guideline — move it to its own file (e.g., components/HeatmapTile.tsx)

  • ⚠️ Hardcoded zIndex: 199 in HeatmapTile's Portal backdrop (DBDashboardPage.tsx:155) may conflict with the dashboard's ZIndexContext — use useZIndex() or the context's value instead of a magic number

  • ⚠️ eslint-disable-next-line react-hooks/exhaustive-deps in HeatmapSettingsDrawer.tsx:1783 suppresses a deps warning on form.reset(defaultValues) — use const formRef = useRef(form) to stable-ref the form object, or call form.reset directly without the effect (Mantine/RHF pattern)

  • ⚠️ buildHeatmapBoundsConfig / buildHeatmapBucketConfig exposed from DBHeatmapChart.tsx — these are internals now on the public API surface. This is needed for the SQL preview, but worth noting the coupling for future refactors

  • 🔎 HeatmapTile container onClick has no keyboard handler — clicking via keyboard (e.g. Tab + Enter) won't open the Event Deltas popover; add onKeyDown and role="button" / tabIndex={0} for a11y parity

  • ✅ Logic, validation, round-trip tests, and formatDurationMsCompact implementation all look correct. The justDraggedAtRef/300ms guard for the post-drag synthetic click is a clean fix.

…le form, revert ChartContainer

- Extract toHeatmapChartConfig() helper into DBHeatmapChart.tsx,
  eliminating duplicated config construction and unsafe casts in
  DBDashboardPage and ChartPreviewPanel
- Export HeatmapChartConfig and HeatmapSelectExtras types for typed
  access to countExpression and heatmapScaleType on select items
- Add useEffect to sync HeatmapSettingsDrawer form when defaultValues
  change (prevents stale closure when source changes while drawer open)
- Fully revert ChartContainer to original (no gap change)

Co-authored-by: Alex Fedotyev <[email protected]>
Copy link
Copy Markdown
Contributor

@pulpdrew pulpdrew left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! A couple more comments.

Also, just re-confirming we have a followup issue for the external dashboards API support?

Also another idea (non blocking) - it would be nice to show the heatmap legend in the chart explorer/dashboard tile version as well.

Comment thread packages/common-utils/src/types.ts Outdated
Comment thread packages/app/src/components/DBEditTimeChartForm/utils.ts
Comment thread packages/app/src/components/DBEditTimeChartForm/EditTimeChartForm.tsx Outdated
Comment thread packages/app/src/components/HeatmapSettingsDrawer.tsx Outdated
Copy link
Copy Markdown
Contributor

@pulpdrew pulpdrew left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! A couple more comments.

Also, just re-confirming we have a followup issue for the external dashboards API support?

Also another idea (non blocking) - it would be nice to show the heatmap legend in the chart explorer/dashboard tile version as well.

- Narrow `heatmapScaleType` schema from `string` to `enum(['log','linear'])`,
  removing the defensive ternaries in DBHeatmapChart and EditTimeChartForm
  that were re-validating the value at runtime.
- Fix tab-switch bug: when entering Heatmap from Search, `select` was still a
  string when the heatmap-defaults branch checked for an array, so the
  defaults never applied and the query errored. Restructure the displayType-
  change effect so the Heatmap branch covers both string and array `select`.
- Drop redundant `as` cast in HeatmapSettingsDrawer scaleType handler in
  favor of an inline type guard.
- Add `showLegend` prop to DBHeatmapChart; enable it on the chart-editor
  preview and dashboard tile so the heatmap shows a compact gradient legend
  in the toolbar (search keeps its existing delta-chart legend).
- Migrate DBSearchHeatmapChart to use `toHeatmapChartConfig` instead of
  building the heatmap config inline, so all three call sites share the
  same transform.
@github-actions github-actions Bot added review/tier-4 Critical — deep review + domain expert sign-off and removed review/tier-3 Standard — full human review required labels Apr 27, 2026
@alex-fedotyev
Copy link
Copy Markdown
Contributor Author

@pulpdrew round 2 pushed in 4e1d46d + 7ec8cad (merge of latest main). Replied on each thread; quick recap:

Addressed in this round

  • scaleType enumz.enum(['log', 'linear']).optional() in DerivedColumnSchema; runtime ternaries collapsed to ?? 'log'.
  • Tab-switch bugselect was captured before the earlier setValue ran, so the heatmap branch's Array.isArray(select) guard silently failed when entering Heatmap from Search. Restructured the effect so the Heatmap branch covers both entry shapes (string from Search/RawSQL, array from other builder tabs).
  • Redundant cast in HeatmapSettingsDrawer — replaced with an inline 'log' | 'linear' type guard.
  • Legend (your non-blocking idea) — added showLegend prop on DBHeatmapChart; enabled on the chart-editor preview and dashboard tile. Search keeps its existing DBDeltaChart legendPrefix so it doesn't duplicate.
  • Less copy-pasteDBSearchHeatmapChart now goes through toHeatmapChartConfig instead of building the heatmap-shaped config inline, so all three call sites (search / editor / dashboard) share the same transform.

External Dashboards API followup

Filed HDX-4120 — External Dashboards API: support DisplayType.Heatmap. Assigned to you, scoped under the External API project, parented to HDX-3993. Covers OpenAPI enum extension, schema for countExpression / heatmapScaleType, trace-source-only validation, integration test, and Playwright round-trip test (per HDX-3311 pattern).

Parked

MV-indicator fix + SQL preview — these are tied together (the right config to feed buildChartConfigForExplanations depends on which of the two queries is meaningful). Posted a breakdown of the two-query architecture with four options for the preview UX and a recommendation; will land both together once you pick a direction.

Heatmap runs as two sequential ClickHouse queries (a bounds query feeding
min/max into a bucketed-counts query), so a single SQL preview misrepresents
what actually executes. Render both queries labeled in the editor's
"Generated SQL" accordion; the bucket query uses {min}/{max} placeholder
tokens since those literals are only known once the bounds query returns.

Also suppress the MV optimization indicator on the heatmap tab — neither of
the two queries is what the editor's chart config alone would produce, so
the indicator was misleading. Per Drew's review feedback on PR #2107.

Refactor: extract buildHeatmapBoundsConfig / buildHeatmapBucketConfig from
DBHeatmapChart's HeatmapContainer to top-level exports so the SQL preview
can reuse them. No behavioral change to the live chart.
Caught by CI lint (no-unused-vars). Local --quiet eslint missed these
because the rule severity differs locally.
@kodiakhq kodiakhq Bot merged commit ef571cc into main Apr 28, 2026
18 checks passed
@kodiakhq kodiakhq Bot deleted the cursor/heatmap-chart-editor-dashboard-5801 branch April 28, 2026 20:37
shaneholloman pushed a commit to shaneholloman/clickhouse-docs that referenced this pull request May 6, 2026
Add a Heatmap step to the Custom Dashboards walkthrough covering:

- when to use a heatmap vs a Line chart (distribution shape over time)
- that the data source dropdown is restricted to Traces sources
  (logs, metrics, and session sources are filtered out, since
  heatmaps need the source's Duration Expression for span duration)
- the pre-filled Value (`(Duration)/1e6`) and Count (`count()`) once a
  source is selected
- using `Where` and the time range to scope the heatmap to a
  service or set of operations the user wants to observe, with
  wider ranges making bimodal latency patterns easier to spot
- the Display Settings drawer (Scale / Value / Count), with a
  cross-link to the Event Deltas page where the full settings
  table already lives
- the two-query model (bounds + bucket) visible in the editor SQL
  preview
- a Drill down to Event Deltas subsection: clicking a cell on a
  rendered tile reveals a "View in Event Deltas" action that opens
  the Event Deltas view with the tile's data source, Where clause,
  and time range carried over

Cross-link the existing Event Deltas "Customize the heatmap"
section to the new dashboard tile flow so users discovering the
feature in either place find the other.

Screenshots are captured in dark theme to match neighbouring
sections, with `ServiceName:"payment"` over 24h to surface the
bimodal duration distribution that makes a heatmap worth using.

Closes ClickHouse#6146

Refs hyperdxio/hyperdx#2107
kodiakhq Bot pushed a commit that referenced this pull request May 7, 2026
## Summary

- Drop the `export` from `HeatmapChartConfig` in `packages/app/src/components/DBHeatmapChart.tsx`. No external file imports the type by name; consumers (`DBDashboardPage`, `ChartPreviewPanel`, `DBSearchHeatmapChart`) use the inferred return of `toHeatmapChartConfig(...)`. The type stays in use locally for the function return shape and prop annotations.

## Why

OSS knip 6.0.1 happens not to flag this. EE's lockfile resolved knip to 6.0.3 after the recent divergence, and the stricter minor flags inferred-only exports as unused. Result: every heatmap-touching downstream merge into `DeploySentinel/hyperdx-ee` fails knip on this single line and needs a manual fixup. Most recent occurrence: DeploySentinel/hyperdx-ee#1997 (heatmap wire-up upstream merge of #2107). Dropping the export here lets the next merge cycle pass cleanly.

Behavior-neutral. No runtime impact. No changeset (not user-visible).

## Test plan

- [x] `yarn nx run @hyperdx/common-utils:ci:build`
- [x] `yarn nx run @hyperdx/app:ci:lint`
- [x] `yarn knip:ci` (`{"issues":[]}`, exit 0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge review/tier-4 Critical — deep review + domain expert sign-off

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants